package demo.java.io;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import demo.vo.common.TimeCost;
import org.junit.Test;

import demo.vo.SerializableVo;

public class SerializeDemo {

    @Test
    public void demo2() throws Exception {
        String s = "1月7日，有网友称在上海中山公园龙之梦的负一层看见大量老鼠，不止一家店，整个负一层内，仅用肉眼就能看到四五只老鼠。记者在网友拍摄的视频中看到，老鼠比一个拳头还要大，不止一只二只，而是有很多只。";
        serialize(s, "D:/logs/aa.utf8");
        byte[] gbk = s.getBytes("gbk");
        System.out.println(Arrays.toString(gbk));
        byte[] utf8 = s.getBytes("utf8");
        System.out.println(Arrays.toString(utf8));
        deSerialize("D:/logs/aa.gbk");
    }

    /**
     * 原始类型会自动转为包装类型进行序列化
     */
    @Test
    public void demo1() {
        String path = "D:/logs/1.txt";
        serialize(1L, path);
        long a = deSerialize(path);
        System.err.println(a);
    }

    @Test
    public void byteToObjectTest() throws UnsupportedEncodingException {
        String h = "中国";
        System.out.println("源：" + h);

        String g = new String(h.getBytes(Charset.forName("GBK")), "GBK");
        String u = new String(h.getBytes(Charset.forName("UTF-8")));
        System.out.println(g);
        System.out.println(u);

        byte[] bg = toByteArray(g);
        byte[] bu = toByteArray(u);

        System.out.println(Arrays.toString(bg));
        System.out.println(Arrays.toString(bu));

        String gb = (String) byteToObject(bg);
        String ub = (String) byteToObject(bu);

        System.out.println(gb);
        System.out.println(ub);
    }

    @Test
    public void test() throws UnsupportedEncodingException {
        String str = "你好，我叫啦啦";
        byte[] array = toByteArray(str);
        System.out.println(Arrays.toString(array));
        byte[] array1 = str.getBytes("UTF8");
        System.out.println("UTF-8:" + Arrays.toString(array1));
        byte[] array2 = str.getBytes("GBK");
        System.out.println("GBK：" + Arrays.toString(array2));

        Object object = byteToObject(array);
        System.out.println((String) object);

        byte[] a = {-84, -19, 0, 5, 116, 0, 21, -28, -67, -96, -27, -91, -67, -17, -68, -116, -26, -120, -111, -27,
                -113, -85, -27, -107, -90, -27, -107, -90};
        object = byteToObject(a);
        System.out.println((String) object);

        String s1 = new String(a);
        System.out.println(s1);

    }

    // 数组转对象
    public static Object byteToObject(byte[] bytes) {
        if (bytes.length < 1) {
            return null;
        }
        Object obj = null;
        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(bis);) {
            obj = ois.readObject();
        } catch (Exception e) {
            e.getStackTrace();
        }
        return obj;
    }

    // 对象转数组
    public static byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos);) {
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
        } catch (IOException e) {
            e.getStackTrace();
        }
        return bytes;
    }

    @Test
    public void testSerializable() {
        Object a = null;
        boolean b = a instanceof Serializable;
        System.out.println("null = " + b);
        a = new byte[0];
        b = a instanceof Serializable;
        System.out.println("数组 = " + b);
        a = new Object();
        b = a instanceof Serializable;
        System.out.println("Object = " + b);
        a = new ArrayList();
        b = a instanceof Serializable;
        System.out.println("ArrayList = " + b);
        a = new ArrayList(Arrays.asList(new Object()));

        b = a instanceof Serializable;
        System.out.println("ArrayList = " + b);

        sum(1, 2, 3);

        String[] s = byteToString("hello".getBytes(), "world".getBytes(), "你好".getBytes());
        System.out.println(Arrays.toString(s));
    }


    public static int sum(int... keys) {
        if (keys instanceof int[]) {
            System.out.println("数组");
        }
        return Arrays.stream(keys).sum();
    }

    public static String[] byteToString(byte[]... keys) {
        if (keys instanceof byte[][]) {
            System.out.println("数组");
        }
        String[] s = new String[keys.length];
        for (int i = 0; i < keys.length; i++) {
            s[i] = new String(keys[i]);
        }
        return s;
    }


    /**
     * 主要用于jedis的参数和结果的类型判断
     */
    private static boolean isSerializable(Object object) {
        if (object == null || !(object instanceof Serializable)) {
            return false;
        }

        if (object instanceof String || object instanceof byte[]) {
            return true;
        }

        if (object instanceof List) {
            List list = (List) object;
            if (list.size() > 0) {
                Object list0 = list.get(0);
                if (list0 instanceof Serializable) {
                    return true;
                }
            }
        }

        if (object instanceof Set) {
            Set set = (Set) object;
            Iterator iterator = set.iterator();
            if (iterator.hasNext()) {
                Object set0 = iterator.next();
                if (set0 instanceof Serializable) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 序列化对象到文件
     *
     * @param t
     * @param path
     */
    public static <T> void serialize(T t, String path) {
        try (ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(path))) {
            objectOutput.writeObject(t);
            objectOutput.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将文件反序列化为Java对象
     *
     * @param path
     * @return
     */
    public static <T> T deSerialize(String path) {
        // Deserialize a string and date from a file.
        try (FileInputStream fileInputStream = new FileInputStream(path);
             ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);) {

            T t = (T) objectInputStream.readObject();
            System.out.println(t);
            return t;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * <h1>transient</h1> A：当对象被序列化时（写入字节序列到目标文件）时，transient阻止实例中那些用此关键字声明的变量持久化；
     * 当对象被反序列化时（从源文件读取字节序列进行重构），这样的实例变量值不会被持久化和恢复。
     * 例如，当反序列化对象——数据流（例如，文件）可能不存在时，原因是你的对象中存在类型为java.io.InputStream的变量，序列化时这些变量引用的输入流无法被打开。
     */
    @Test
    public void transientDemo() {
        SerializableVo user = new SerializableVo();
        user.setName("Alexia");
        user.setPasswd("123456");

        System.out.println("read before Serializable: ");
        System.out.println("username: " + user.getName());
        System.err.println("password: " + user.getPasswd());

        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("D:/logs/user.txt"));
            os.writeObject(user); // 将User对象写进文件
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:/logs/user.txt"));
            user = (SerializableVo) is.readObject(); // 从流中读取User的数据
            is.close();

            System.out.println("\nread after Serializable: ");
            System.out.println("username: " + user.getName());
            System.err.println("password: " + user.getPasswd());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    static void test2() {

        List<String> list = new ArrayList<String>();
        list.add("Stan");
        list.add("hello");
        list.add("world");
        SerializableVo bean = new SerializableVo();
        bean.setList(list);
        bean.setName("Stan");
        bean.setToday(new Date());
        bean.setAge(30);
        bean.setMoney(new Double(100000000000.00));
        String path = "d:/cache/today.txt";
        serialize(bean, path);
        SerializableVo tmp_bean = deSerialize(path);
    }

    static void test1() {
        // Serialize today's date to a file.
        try (ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("d:/cache/today.txt"))) {
            objectOutput.writeObject("Today");
            objectOutput.writeObject(new Date());
            objectOutput.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // Deserialize a string and date from a file.
        try (FileInputStream fileInputStream = new FileInputStream("d:/cache/today.txt");
             ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);) {

            String today = (String) objectInputStream.readObject();
            Date date = (Date) objectInputStream.readObject();
            System.out.println(today);
            System.out.println(date);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 对象序列化后的大小
     *
     * @param obj 对象
     * @return
     */
    public static int objectSize(Object obj) {
        if (obj == null) {
            return 0;
        }
        byte[] bytes = new byte[0];
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bytes.length;
    }

    @Test
    public void testObjectSize() {
        String s = IntStream.range(0, 1024).mapToObj(i -> "a").collect(Collectors.joining());
        TimeCost t0 = new TimeCost();
        int count = 1000_000;
        for (int i = 0; i < count; i++) {
            objectSize(s);
        }
        System.out.println(count + "次String序列化耗时：" + t0.cost());

        t0.reset();
        for (int i = 0; i < count; i++) {
            int size = s.getBytes().length;
        }
        System.out.println(count + "次string.getBytes耗时：" + t0.cost());
    }

    @Test
    public void testObjectSize2() {
        byte[] s = new byte[1024];
        TimeCost t0 = new TimeCost();
        int count = 1000_000;
        for (int i = 0; i < count; i++) {
            objectSize(s);
        }
        System.out.println(count + "次byte[]序列化耗时：" + t0.cost());

        t0.reset();
        for (int i = 0; i < count; i++) {
            int size = s.length;
        }
        System.out.println(count + "次byte[].length耗时：" + t0.cost());
    }


    @Test
    public void testObjectSize3() {
        System.out.println("byte: " + objectSize(Byte.MAX_VALUE));
        System.out.println("Short: " + objectSize(Short.MAX_VALUE));
        System.out.println("Integer: " + objectSize(Integer.MAX_VALUE));
        System.out.println("Long: " + objectSize(Long.MAX_VALUE));
        System.out.println("Double: " + objectSize(Double.MAX_VALUE));

    }
}
